interface Shape {
    draw(): void;
}

class Rectangle implements Shape {
    public draw(): void {
        console.log('▲');
    };
}

class Square implements Shape {
    public draw(): void {
        console.log('□');
    };
}

class Circle implements Shape {
    public draw(): void {
        console.log('o');
    };
}

interface Color {
    fill(): void;
}

class Red implements Color {
    public fill(): void {
        console.log('red');
    }
}

class Green implements Color {
    public fill(): void {
        console.log('green');
    }
}

class Blue implements Color {
    public fill(): void {
        console.log('blue');
    }
}

interface AbstractFactory {
    getColor(color: string): Color | null;
    getShape(shape: string): Shape | null;
}

class ShapeFactory implements AbstractFactory {
    public getShape(shape: string): Shape | null {
        if (shape === null) {
            return null;
        }
        switch (shape.toLowerCase()) {
            case 'circle':
                return new Circle();
            case 'rectangle':
                return new Rectangle();
            case 'square':
                return new Square();
            default:
                return null;
        }
    }
    public getColor(_: string): null {
        return null;
    }
}

class ColorFactory implements AbstractFactory {
    public getColor(color: string): Color | null {
        if (color === null) {
            return null;
        }
        switch (color.toLowerCase()) {
            case 'red':
                return new Red();
            case 'green':
                return new Green();
            case 'blue':
                return new Blue();
            default:
                return null;
        }
    }
    public getShape(_: string): null {
        return null;
    }
}

class FactoryProducer {
    public static getFactory(choice: string): AbstractFactory | null {
        switch (choice) {
            case 'SHAPE':
                return new ShapeFactory();
            case 'COLOR':
                return new ColorFactory();
            default:
                return null;
        }
    }
}

function main() {
    const sf: AbstractFactory = FactoryProducer.getFactory('SHAPE') as AbstractFactory;
    const s1: Shape = sf.getShape('CIRCLE') as Shape;
    s1.draw();
    const s2: Shape = sf.getShape('RECTANGLE') as Shape;
    s2.draw();
    const s3: Shape = sf.getShape('SQUARE') as Shape;
    s3.draw();

    const cf: AbstractFactory = FactoryProducer.getFactory('COLOR') as AbstractFactory;
    const c1: Color = cf.getColor('green') as Color;
    c1.fill();
    const c2: Color = cf.getColor('blue') as Color;
    c2.fill();
    const c3: Color = cf.getColor('red') as Color;
    c3.fill();
}

main();
